package com.ayoldi.podcastusbsync.downloader;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;


import org.apache.log4j.Logger;
import org.joda.time.DateTime;

import com.ayoldi.podcastusbsync.configurator.ConfigurationManager;
import com.ayoldi.podcastusbsync.configurator.ConfigurationUtils;
import com.ayoldi.podcastusbsync.model.Configuration;
import com.ayoldi.podcastusbsync.model.Configuration.UsbDeviceConfiguration.PodcastFeed;
import com.sun.syndication.feed.synd.SyndEnclosure;
import com.sun.syndication.feed.synd.SyndEnclosureImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

public class FeedsDownloaderThread extends Thread{

	static Logger LOG = Logger.getLogger(FeedsDownloaderThread.class);
	
	private static final int FIRST_TIME_DOWNLOAD_FEEDS=10;
	private static final long DEFAULT_FEED_MONITOR_PERIOD=120000;
	
	Configuration configuration;
	File configFile;
	long feedMonitorPeriod;
	ConfigurationManager configManager;
	
	boolean stopped=false;
	
	public boolean isStopped() {
		return stopped;
	}

	public void setStopped(boolean stopped) {
		this.stopped = stopped;
	}



	public static final String[] ALLOWED_EXTENSIONS=new String[]{"wav","mp3"};
	
	public FeedsDownloaderThread (Configuration configuration, File configFile, ConfigurationManager configManager){
		this.configuration=configuration;
		this.configFile=configFile;
		this.configManager=configManager;
		try{
			feedMonitorPeriod=Long.parseLong(ConfigurationUtils.getGlobalOptions(configuration).getFeedMonitorPeriod())*1000;

		}
		catch (Exception e){
			LOG.warn("Could not parse feed monitor period: "+ ConfigurationUtils.getGlobalOptions(configuration).getFeedMonitorPeriod()+". Using default "+DEFAULT_FEED_MONITOR_PERIOD);
		}
	}
	
	public void run() {
		while (!stopped){
			downloadNewFeeds();		
			try {
				Thread.sleep(feedMonitorPeriod);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		
	}
	
	private void downloadNewFeeds(){
	
		LOG.info("Updating feeds...");
		
		for (PodcastFeed feed : ConfigurationUtils.getFeeds(configuration) ){
			LOG.info("Updating feed: "+feed.getName());
			updateFeed(feed);			
		}		
		
	}
	
	private void updateFeed(PodcastFeed feed){
		URL url;

		DateTime newLastCheckedDateTime;
		DateTime lastCheckedDate=null;
		boolean allDownloadsSuccessful=false;
		lastCheckedDate=FeedsDownloaderUtils.parseDate(feed.getLastChecked());
		try {
			url = new URL(feed.getUrl());
	        SyndFeedInput input = new SyndFeedInput();
	        SyndFeed feedInternal = input.build(new XmlReader(url));

			
			List<SyndEntry> feedEntries=feedInternal.getEntries();
			List<SyndEntry> feedEntriesToDownload=new ArrayList<SyndEntry>();
			
			if (feed.getLastChecked()!=null){				
				LOG.info("Feed last checked on: "+feed.getLastChecked());
				for (SyndEntry entry : feedEntries) {
					DateTime entryDateTime=new DateTime(entry.getPublishedDate());					
					if (entryDateTime.isAfter(lastCheckedDate)){
						feedEntriesToDownload.add(entry);
						LOG.info("New entry found after last checked: "+entry.getTitle());
					}
					else{
						LOG.info("Entry already crawled (before last checked): "+entry.getTitle());
					}
					
				}				
				
			}
			else{
				LOG.info("Feed never checked before, getting the last "+FIRST_TIME_DOWNLOAD_FEEDS+" entries.");
				Collections.sort(feedEntries, new FeedPubDateComparator());
				feedEntriesToDownload.addAll(feedEntries.subList(0, FIRST_TIME_DOWNLOAD_FEEDS));
			}
			
			feedEntriesToDownload=FeedsDownloaderUtils.filterListByFeedRegexp(feedEntriesToDownload, feed);
			
			newLastCheckedDateTime=new DateTime();
			allDownloadsSuccessful=downloadEntries(feedEntriesToDownload, ConfigurationUtils.getAbsoluteFeedDownloadFolder(ConfigurationUtils.getLocalSyncFolder(configuration).getAbsolutePath(), feed.getFolder()),feed.getRenameRule(),feed.getRegex());
			
		} catch (Exception e) {
			e.printStackTrace();
			return;
		} 
		
		if (allDownloadsSuccessful){
			FeedsDownloaderUtils.updateLastCheckedTag(newLastCheckedDateTime,feed, this.configuration, this.configFile,configManager);		

		}
		
	}
	
	private static boolean downloadEntries(List<SyndEntry> feedEntriesToDownload, File path, String regexRenameRule, String titleFilterRegex) {
		LOG.info("Downloading "+feedEntriesToDownload.size()+" entries to "+path.getAbsolutePath());
		boolean success=true;
		for (SyndEntry entry : feedEntriesToDownload){
			
			try {
				URL downloadURL=null;
				if (entry.getLink()!=null && FeedsDownloaderUtils.hasAllowedExtension(ALLOWED_EXTENSIONS, entry.getLink())){
					downloadURL=new URL(entry.getLink());
				}
				else if (entry.getUri()!=null && FeedsDownloaderUtils.hasAllowedExtension(ALLOWED_EXTENSIONS, entry.getUri())){
					downloadURL=new URL(entry.getUri());
				}
				else if (entry.getEnclosures()!=null && entry.getEnclosures().size()>0){
					for (Object enclosure : entry.getEnclosures()){
						String enclosureURL=((SyndEnclosure)enclosure).getUrl();
						if (enclosureURL!=null && FeedsDownloaderUtils.hasAllowedExtension(ALLOWED_EXTENSIONS, enclosureURL)){
							downloadURL=new URL(enclosureURL);
						}
					}						
						
				}
				else{
					continue;
				}
				LOG.info("Downloading "+entry.getTitle());
				FeedsDownloaderUtils.downloadFile(downloadURL,path,false,FeedsDownloaderUtils.getFileNameFromTitle(entry.getTitle(), regexRenameRule,titleFilterRegex));
			} catch (MalformedURLException e) {
				e.printStackTrace();
				success=false;
				continue;
			}
		}
		return success;
		
	}



	private static class FeedPubDateComparator implements Comparator{

		public int compare(Object o1, Object o2) {
			if (!o1.getClass().equals(SyndEntry.class) || !o2.getClass().equals(SyndEntry.class)){
				return 0;
			}
			
			Date date1=((SyndEntry)o1).getPublishedDate();
			Date date2=((SyndEntry)o2).getPublishedDate();			
			
			long n1 = date1.getTime();
	        long n2 = date2.getTime();
	        if (n1 < n2) return -1;
	        else if (n1 > n2) return 1;
	        else return 0;
		}
		
	}
	
}
